/*
 * HD.h
 *
 *  Created on: Jul 7, 2009
 *      Author: elkobir
 */

#ifndef HD_H_
#define HD_H_

#include <iostream>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <iostream>
#include <string>
#include <iomanip>
#include <fstream>
#include <vector>
#include <sstream>
#include "Utils.h"
#include "MetadataBlock.h"
#include "INode.h"
#include "BlockNode.h"
#include "Utils.h"
#include "Log.h"
using namespace std;

#define INODESIZE 50
#define FREE_BLOCKS_BLOCK 1
#define FREE_INODES_BLOCK 2


class HD {
private:
	int _fd;
	MetadataBlock* _metadataBlock;
	BlockNode* _freeINodesBlock;
	BlockNode* _freeBlocksBlock;


	BlockNode* loadBlockNode(int dblockNum);
	void saveBlockNode(BlockNode* blockNode, int dblockNum);
	INode* loadINode(int i_nodeNum);
	void saveINode(INode* i_node, int i_nodeNum);
	INodeIndirect* loadIndirect(int dblockNum);
	void saveIndirect(INodeIndirect* indirect, int dblockNum);

	int allocateAddress(BlockNode* blockNode, int blockType);
	void reload();
	void flush();
	void enter();
	void leave();

	void freeDataBlockUnsync(int dblock);
	void readBlockUnsync(int dblockNum, unsigned char* buf);
	void writeBlockUnsync(int dblockNum, unsigned char* newdata);
	int allocateDataBlockUnsync();
	int allocateInodeUnsync();

	void initFreeBlocks();
	void initFreeInodes();
	void initAll();
    void addToFreeINodesList(int i_nodeNum);
    void addToFreeBlocksList(int i_nodeNum);

	pthread_mutex_t _lock;
public:

	HD(int dataBlockSize, int numberOfInodes, unsigned long int diskSize);
    HD();

	int _BlockSizeInBytes;

    virtual ~HD();
    // allocates a new i-node and returns its number
    int allocateInode();
    // frees the specified i-node and also frees all the data blocks pointed to by it (directly or indirectly).
    void freeInode(int i_node);

    // allocates a data block and returns its number
    int allocateDataBlock();

    // frees the specified block. Use (and update) the maps in the super block in these allocate/free methods.
    void freeDataBlock(int dblock);

    // return the type of the file (normal/directory/softlink).
    int getInodeType(int i_nodeNum);

    // set the type of the file (normal/directory/softlink).
    void setInodeType(int i_nodeNum, int filetype);

    /* return the number of the ith data block containing the data of this file
    * (for 0<=i<=9 this is just stored in the 10 pointers in this inode, for i>9,
    * you must retrieve it from the indirect data block..). */
    int getDataBlock (int i_nodeNum, int i);

    // sets the ith data block of this file to be dblockNum.
    void setDataBlock (int i_nodeNum, int i, int dblockNum);

    // read the entire content of the dblockNum's and copy it into buf.
    void readBlock(int dblockNum, unsigned char* buf);

    /* overwrite the content of block with number dblockNum with the newdata block.
    * Assume newdata's length is equal to dataBlockSize. */
    void writeBlock(int dblockNum, unsigned char* newdata);

    /* return the file size saved in the given inode. This is the exact size of the file in bytes,
    * not the number of blocks used by this file. */
    int getFileSize(int i_nodeNum);

    // set the file size.
    void setFileSize(int i_nodeNum, int newSize);

    int incHardLinks(int i_nodeNum);

    int decHardLinks(int i_nodeNum);

    int getMaxFileSizeInBytes();
    int getTotalINodeCount();
    int getRootInodeNum();
    int initializeRoot();


};


#endif /* HD_H_ */
